package dolf.objects;

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Stroke;
import dolf.objects.WorldObject;

public class ArcObject extends WorldObject implements java.io.Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int radius;
	private int angle1;
	private int angle2;
	private CircleObject P1, P2;
	private final int LINE_WIDTH = 2;
	public Point o;
	public ArcObject(Point pos, int rad, int _angle1, int _angle2){
		super(false);
		this.setPosition(pos);
		this.angle1 = _angle1 > 180 ? (_angle1-360) : _angle1;
		this.angle2 = _angle2;
		this.radius = rad;
		 this.P1 = new CircleObject(this.getP1(), this.LINE_WIDTH/2,false);
		 this.P2 = new CircleObject(this.getP2(), this.LINE_WIDTH/2,false);
	}
	public ArcObject(int rad, int x1, int y1, int x2, int y2){
		super(false);
		this.createObject(rad, new Point(x1, y1), new Point(x2,y2));// if false exception
		 this.P1 = new CircleObject(this.getP1(), this.LINE_WIDTH/2,false);
		 this.P2 = new CircleObject(this.getP2(), this.LINE_WIDTH/2,false);
	}
	public ArcObject(int rad, Point p1, Point p2){
		super(false);
		this.createObject(rad, p1, p2);	// if false throw exception
		 this.P1 = new CircleObject(this.getP1(), this.LINE_WIDTH/2,false);
		 this.P2 = new CircleObject(this.getP2(), this.LINE_WIDTH/2,false);
	}
	
	private boolean createObject(int rad, Point p1, Point p2){
		this.radius = Math.abs(rad);
		double tmp = p1.getDist(p2)/2;
		Vector p1p2 = p1.sub(p2); // vektor von p2 zu p1
		Vector norm = p1p2.getNormVec(); // senktrechter vektro zum p1p2
		double wurzel = (rad*rad) - (tmp*tmp);
		if(wurzel<0){
			return false;
		}
		Vector p = norm.setLength(Math.sqrt(wurzel)* (rad<0?-1:1) );// vektor von mitte p1p2 zum mittelpunkt
		Point phelp = p1.add(p2.sub(p1).setLength(tmp)); // mittelpunkt von zwischen p1p2
		this.setPosition(phelp.add(p)); //fertiger mittelpunkt
		this.angle1 =-(int) (Math.atan2(p1.getY()-this.getPosition().getY(),p1.getX()-this.getPosition().getX())/Math.PI*180);
		this.angle2 =360-this.angle1 -(int) (Math.atan2(p2.getY()-this.getPosition().getY(),p2.getX()-this.getPosition().getX())/Math.PI*180);
		return true;
	}
	
	public int getRadius(){
		return this.radius;
	}
	public int getLineWidth(){
		return this.LINE_WIDTH;
	}
	public int getAngle1(){
		return this.angle1;
	}
	public int getAngle2(){
		return this.angle2;
	}
	public double getAngle1Rad(){
		return this.angle1*Math.PI/180;
	}
	public double getAngle2Rad(){
		return this.angle2*Math.PI/180;
	}
	public Point getP1(){
		return new Point(this.getPosition().getX()+(Math.cos((this.getAngle1()*Math.PI)/180)*this.getRadius()),this.getPosition().getY()-(Math.sin((this.getAngle1()*Math.PI)/180)*this.getRadius()));
	}
	public Point getP2(){
		return new Point(this.getPosition().getX()+(Math.cos((this.getAngle1()+this.getAngle2())*Math.PI/180)*this.getRadius()),this.getPosition().getY()-(Math.sin((this.getAngle1()+this.getAngle2())*Math.PI/180)*this.getRadius()));
	}
	public CircleObject getCircleP1(){
		return P1;
	}
	public CircleObject getCircleP2(){
		return P2;
	}
	public void paint(Graphics2D g){
		int r = this.LINE_WIDTH/2;
		g.drawOval(this.getP1().getIntX()-r,this.getP1().getIntY()-r,r*2,r*2);
		g.drawOval(this.getP2().getIntX()-r,this.getP2().getIntY()-r,r*2,r*2);

		Stroke tmp = g.getStroke();
		g.setStroke(new BasicStroke(LINE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL ));
		g.drawArc(this.getPosition().getIntX()-this.getRadius(), this.getPosition().getIntY()-this.getRadius(), 2*this.getRadius(), 2*this.getRadius(), this.angle1, this.angle2);
		g.setStroke(tmp);
		if(o != null)
			g.drawOval(this.o.getIntX(), this.o.getIntY(), 1, 1);	
	} 
	public boolean isInside(Point p){
		if(p.getDist(this.getPosition())<this.getRadius()){
			return true;
		} else {
			return false; 
		}
	}
	public boolean newMotion(WorldObject object){
		//System.out.println("koll");
		CircleObject obj = (CircleObject) object;
		//Point[] s;
		
		CircleObject c1 = this.getCircleP1();
		double d = obj.kollCircle(c1);
		if(d == 0){
			System.out.println("apprall Kugel");
			CircleKoll(this.getP1(), obj);
			return true;
		}
		
		CircleObject c2 = this.getCircleP2();
		if(d>0){
			System.out.println(d);
		}
		d = obj.kollCircle(c2);
		
		if(d == 0){
			System.out.println("apprall Kugel");
			CircleKoll(this.getP2(), obj);
			return true;
		}
		//NormKoll(obj);
		CircleKoll(this.getPosition(), obj);
		if(this.isInside(obj.getPosition())){
			obj.setMotion(obj.getMotion().multS(-1));
		}
		if(d>0){
			System.out.println(d);
		}
		return true;
	}

	//kollision mit Endpunkten
	private void CircleKoll(Point koll,CircleObject obj){
		//obj.setMotion(obj.getMotion().setLength(obj.getMaxSpeed()-dist));
	    Vector impact;
	    double tmp = obj.getMotion().getLength();
		impact = obj.getMotion().multS(-1);  
	    Vector impulse = koll.sub(obj.getPosition()).getNormed();	
	    double ImpactSpeed = impact.multX(impulse); 
	    impulse = impulse.multS(Math.sqrt(Math.abs(ImpactSpeed) *20*  obj.getMass()));
		obj.setMotion(obj.getMotion().sub(impulse.multS(1/obj.getMass())));
		obj.setMotion(obj.getMotion().setLength(tmp));
		/*if(obj instanceof Ball){
			((Ball)obj).addCollition(this);
		}*/
	}
	// kollision einer keises(vom ArcObject) mit einer geraden(Punkt und Richtung) 
	// fakt ist der modifiziert radius des ArcObjects abhaengig vom circleObject radius und der lage des balls innen oder aussen
	// 
	public Point[] normIntersect(Point pos, Vector direction, int fakt){
		Point tmp = pos.sub(this.getPosition());
		double[] merk= new double[3];
		//ganze zahlen (c)
		merk[0]=tmp.getX()*tmp.getX() + tmp.getY()*tmp.getY() - fakt*fakt;
		//mit t (b)
		merk[1]= 2*tmp.getX()*direction.getX() + 2*tmp.getY()*direction.getY();
		//mit t^2 (a)
		merk[2]=direction.multX(direction);
		double sqr, sqrt;
		int w = (int)Math.signum(sqr=(merk[1]*merk[1]-4*merk[0]*merk[2]));
		//System.out.println(w);
		Point[] ret;
		switch (w){
		case 1:
			ret = new Point[2];
			ret[0]=pos.add(direction.multS((-merk[1]+(sqrt = Math.sqrt(sqr)))/2/merk[2]));
			ret[1]=pos.add(direction.multS((-merk[1]-sqrt)/2/merk[2]));
			return ret;
		case -1:
			return null;
		case 0:
			Point S = pos.add(direction.multS((-merk[1]/(2*merk[2]))));
			ret = new Point[1];
			ret[0]=S;
			return ret;
		}
		return null;
	}
	//berechnet den aufprallpunkte des CirclleObjectes mit dem ArcObject
	public Point[] Intersect(Point pos, Vector direction, int radius){
		int radi;
		if(this.isInside(pos)){
			radi = this.getRadius()- radius;
		} else {
			radi = this.getRadius()+ radius; 
		}
		return this.normIntersect(pos, direction, radi);
	}
	// berechnet den aufbrallpunkte des CircleOjectes mit dem ArcObject
	public Point[] Intersect(CircleObject lo){
		return Intersect(lo.getPosition(), lo.getMotion(), lo.getRadius());
	}
	// gibt zureueck ob sich Punkt p auf dem CircleObject befindet
	public boolean onArc(Point p){
		System.out.println(" dist arc : " +this.getPosition().getDist(p));

		if(Math.abs(this.getPosition().getDist(p)-this.getRadius())<1.0E-13){//punkt liegt auf dem radius fehler eingeschossen
			return false;	
		}
		Vector tmp = p.sub(this.getPosition());
		double angle = Math.atan2(tmp.getY(), tmp.getX());
		System.out.println("angle : "+ angle + " 1. arcWinkel : "+this.getAngle1Rad() + " 2. arc Winkel: "+ this.getAngle2Rad());
		if(angle >= this.getAngle1Rad() && angle-this.getAngle1Rad()<= this.getAngle2Rad()){
			return true;
		} else {
			if(p.getDist(this.getCircleP1())>this.getCircleP1().getRadius() && p.getDist(this.getCircleP2())>this.getCircleP2().getRadius()){
				return false;
			} else {
				return true;
			}
		}
	}
}

